import {
  cloneElement,
  forwardRef,
  HTMLAttributes,
  HtmlHTMLAttributes,
  isValidElement,
  LegacyRef,
  ReactNode,
} from "react";
import {
  Draggable,
  DraggableProps,
  DraggableProvided,
  DraggableProvidedDraggableProps,
  Droppable,
  DroppableProps,
  DroppableProvided,
  DroppableProvidedProps,
} from "react-beautiful-dnd";

type DropProps = Omit<DroppableProps, "children"> & {
  children: ReactNode & DropChildProps;
};
export const Drop = ({ children, ...props }: DropProps) => {
  return (
    <Droppable {...props}>
      {(provided: DroppableProvided) => {
        if (isValidElement(children)) {
          return cloneElement(children, {
            ...provided.droppableProps,
            ref: provided.innerRef,
            provided,
          });
        }
        return <div />;
      }}
    </Droppable>
  );
};

type DropChildProps = Partial<
  { provided: DroppableProvided } & DroppableProvidedProps
> &
  React.HTMLAttributes<HTMLDivElement>;
export const DropChild = forwardRef<HTMLDivElement, DropChildProps>(
  ({ children, provided, ...props }, ref) => (
    <div ref={ref} {...props}>
      {children}
      {provided?.placeholder}
    </div>
  )
);

type DragProps = Omit<DraggableProps, "children"> & {
  children: ReactNode & DragChildProps;
};
export const Drag = ({ children, ...props }: DragProps) => {
  return (
    <Draggable {...props}>
      {(provided: DraggableProvided) => {
        if (isValidElement(children)) {
          return cloneElement(children, {
            ...provided.draggableProps,
            ref: provided.innerRef,
            ...provided.dragHandleProps,
            provided,
          });
        }
        return <div />;
      }}
    </Draggable>
  );
};

type DragChildProps = Partial<
  { provided: DraggableProvided } & DraggableProvidedDraggableProps
> &
  HtmlHTMLAttributes<HTMLDivElement>;
export const DragChild = forwardRef<HTMLDivElement, DragChildProps>(
  ({ children, provided, ...props }, ref) => (
    <div ref={ref} {...props}>
      {children}
    </div>
  )
);
